package snmp;

require 5.004;

use Carp 'croak', 'carp';
use strict;
use Net::SNMP;
use Net::SNMP::Security::USM 2.0.0;
use Socket;
use vars qw($VERSION);

$VERSION = "1.5";

sub snmp::new {
   my ($class) = @_;
   my $self = {
      _user => undef,
      _authkey => undef,
      _authpasswd => undef,
      _authproto => undef,
      _privkey => undef,
      _privpasswd => undef,
      _privproto => undef,
      _device => undef,
      _community => undef,
      _timeout => 3,
      _retries => 1,
      _session => undef,
      _error => 0,
      _errmesg => undef,
      _version => undef,
   };

   bless ($self, $class);
   return $self;
}

sub snmp::get_error ($)
{
   my ($self) = @_;
   return $self->{_errmesg};
}

sub snmp::set_timeout ($)
{
   my (($self), $timeout) = @_;
   if (defined($timeout)) {
      $self->{_timeout} = $timeout;
   }
}

sub snmp::set_retries ($)
{
   my (($self), $retries) = @_;
   if (defined($retries)) {
      $self->{_retries} = $retries;
   }
}

sub snmp::get_session ($)
{
   my ($self) = @_;
   my $session = undef;
   if ($self->{_error} == 0) {
      $session = $self->{_session};
   }

   return $session;
}

sub snmp::set_v3 ($$$$$$$$)
{
   my ($self) = @_;
   my $host = shift || "localhost";
   my $username = shift || "initial";
   my $engine = shift;
   my $authpasswd = shift;
   my $authproto = shift || "sha";
   my $privpasswd = shift || "des";
   my $privproto = shift;

   if (defined($host)) {
      $self->{_device} = $host;
   }

   if (defined($username)) {
      $self->{_user} = $username;
   }

   if (defined($authpasswd)) {
      $self->{_authpasswd} = $authpasswd;
   }

   if (defined($authproto)) {
      $self->{_authproto} = $authproto;
   }

   if (defined($privpasswd)) {
      $self->{_privpasswd} = $privpasswd;
   }

   if (defined($privproto)) {
      $self->{_privproto} = $privproto;
   }

   my ($usm, $error) = Net::SNMP::Security::USM->new (
      -username => $self->{_username},
      -authprotocol => $self->{_authproto},
      -authpassword => $self->{_authpasswd},
      -engineid => $engine,
      -privprotocol => $self->{_privproto},
      -privpassword => $self->{_privpassword});

   if (!defined($usm)) {
      $self->{_errmesg} = sprintf("%s", $error);
      $self->{_error} = 1;
   }
   else {
      $self->{_version} = "snmpv3";
      $self->{_authkey} = unpack("H*", $usm->auth_key);
      $self->{_privkey} = unpack("H*", $usm->priv_key);
   }

   return $self->{_error};
}

sub snmp::set_v2 ($$$$)
{
   my (($self), $version, $host, $community) = @_;

   if (($version == 2) or ($version == 1)) {
      if ($version == 2) {
         $self->{_version} = 'snmpv2c';
      }
      elsif ($version == 1) {
         $self->{_version} = "snmpv1";
      }
      else {
         $self->{_errmesg} = "unknown snmp version";
         $self->{_error} = 1;
      }
   }

   if ($self->{_error} == 0) {
      if (defined($community)) {
         $self->{_community} = $community;
      }
      else {
         $self->{_errmesg} = sprintf("no community defined for %s", $version);
         $self->{_error} = 1;
      }
   }

   if ($self->{_error} == 0) {
      if (defined($host)) {
         $self->{_device} = $host;
      }
      else {
         $self->{_errmesg} = sprintf(
            "no host defined for community: %s", $community);
         $self->{_error} = 1;
      }
   }

   return $self->{_error};
}

sub snmp::connect ($)
{
   my ($self) = @_;

   if (!defined($self->{_timeout})) {
      $self->{_timeout} = 3;
   }

   if (!defined($self->{_retries})) {
      $self->{_retries} = 1;
   }

   if ($self->{_error} == 0) {
      my $error = undef;
      if ($self->{_version} eq "snmpv3") {
         ($self->{_session}, $error) = Net::SNMP->session( 
            -hostname => $self->{_device},
            -version => $self->{_version} 
            -username => $self->{_user},
            -authkey => $self->{_authkey},
            -authpassword => $self->{_authpasswd},
            -authprotocol => $self->{_authproto},
            -privkey => $self->{_privkey},
            -privpassword => $self->{_privpasswd},
            -privprotocol => $self->{_privproto});
      }
      elsif (($self->{_version} eq "snmpv2c") or 
             ($self->{_version} eq "snmpv1")) {
         ($self->{_session}, $error) = Net::SNMP->session( 
            -hostname => $self->{_device},
            -community => $self->{_community},
            -version => $self->{_version},
            -timeout => $self->{_timeout},
            -retries => $self->{_retries});
      }
      else { 
         $self->{_errmesg} = "unknown snmp version defined";
         $self->{_error} = 1;
      }

      if (!defined($self->{_session})) { 
         $self->{_errmesg} = sprintf("error creating snmp session: %s", $error);
         $self->{_error} = 1;
      }
   }

   return $self->{_error};
}

sub snmp::close ($)
{
   my ($self) = @_;

   if (defined($self->{_session})) {
      $self->{_session}->close;
   }

   while (my($key, $value) = each (%$self)) {
      if ($key eq "_error") {
         $self->{$key} = 0;
      }
      elsif ($key eq "_timeout") {
         $self->{$key} = 3;
      }
      elsif ($key eq "_retries") {
         $self->{$key} = 1;
      }
      else {
         $self->{$key} = undef;
      }
   }
}

sub snmp::get_sys_descr ($)
{
   my ($self) = @_;
   my $sysDescr = undef;

   if ($self->{_error} == 0) {
      my $sysDescrMib = "1.3.6.1.2.1.1.1.0";
      my $sysDescrVal = $self->{_session}->get_request(
         -varbindlist => [$sysDescrMib]);
      if (!defined($sysDescrVal)) {
         $self->{_errmesg} = sprintf("%s", $self->{_session}->error);
         $self->{_error} = 1;
      }  
      else { 
         $sysDescr = $sysDescrVal->{$sysDescrMib}; 
      }
   }

   return $sysDescr;
}

sub snmp::get_sys_uptime ($)
{
   my ($self) = @_;
   my $sys_uptime = undef;

   if ($self->{_error} == 0) {
      my $sysUptimeMib = "1.3.6.1.2.1.1.3.0";
      my $retval = $self->{_session}->get_request(
         -varbindlist => [$sysUptimeMib]);
      if (!defined($retval)) {
         $self->{_errmesg} = sprintf("error: %s", $self->{_session}->error);
         $self->{_error} = 1;
      }
      else {
         $sys_uptime = $retval->{$sysUptimeMib};
      }
   }

   return $sys_uptime;
}

sub snmp::walk ($$)
{
   my (($self), $oid) = @_;
   my @return_table = ();

   if ($self->{_error} == 0) {
      my $retval = $self->{_session}->get_table(-baseoid => $oid); 
      if (!defined($retval)) {
         $self->{_errmesg} = sprintf("error: %s", $self->{_session}->error);
      }
      else {
         my $ix = 0;
         foreach my $key (keys %$retval) {
            $return_table[$ix++] = $$retval{$key};
         }
      }
   }

   return @return_table;
}

1;
